#include <iostream>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <cmath>
#include <time.h>

#include "opencv2/opencv.hpp"

using namespace cv;
using namespace std;

#define NUMBER_OF_TRAINING_SAMPLES 5000
#define ATTRIBUTES_PER_SAMPLE 2  // not the first two as patient ID and class
#define NUMBER_OF_TESTING_SAMPLES 5000

#define NUMBER_OF_CLASSES 2
static char CLASSES[2] = {'R', 'N'};

float maximum(float a[5])
{
	float max=a[0];
	for(int i=1;i<5;i++)
	{
		if(a[i]>max)
			max=a[i];
	}
	return max;
}
float minimum(float a[5])
{
	float min=a[0];
	for(int i=1;i<5;i++)
	{
		if(a[i]<min)
			min=a[i];
	}
	return min;
}
float average(float a[5])
{
	float mean=0.0;
	for(int i=0;i<5;i++)
		mean+=(a[i]/5);
	return mean;
}
float Average(vector<float> v)
{ 
	float sum=0;
		if(v.size()==0) return 0;
		else
		{
			for(int i=0;i<v.size();i++)
               sum+=v[i];
           return sum/v.size();
		}
}
float deviation(vector<float> v)
{
	float E=0;
	float ave=Average(v);
	if(v.size()==0) return 0;
	else
	{
    for(int i=0;i<v.size();i++)
            E+=(v[i] - ave)*(v[i] - ave);
    return sqrt(E/v.size());
	}
}
float spreads(float a[5])
{
	float mean=average(a);
	
	vector<float> ab,be;

	for(int i=0;i<5;i++)
		if(a[i]<mean)
			ab.push_back(a[i]);
		else
			be.push_back(a[i]);

	float std_ab=0,std_be=0;
	std_ab=deviation(ab);
	std_be=deviation(be);

	return(abs(std_ab-std_be));
}

void mergefile(const char* file1, const char* file2,char* file3,int n_samples)
{
	FILE *f1=fopen(file1,"r");
	FILE *f2=fopen(file2,"r");
	FILE *f3=fopen(file3,"r");
	FILE *f4=fopen("feature.txt","w");

	float x1,x2,x3;
	char ch;

	if( !f1 || !f2 || !f3 || !f4 )
    {
        printf("ERROR: Opening training files.\n");
        exit(0); // all not OK
    }
	for(int line = 0; line < n_samples; line++)
	{

		fscanf(f1, "%f,", &x1);
		fscanf(f2, "%f,", &x2);
		fscanf(f3, "%f,", &x3);
		if (x3==1.0)
			ch='R';
		else
			ch='N';
		fprintf(f4,"%c %f %f\n",ch,x1,x2);
	}
	fclose(f1);fclose(f2);fclose(f3);fclose(f4);
}

void mergefile_main(const char* file1, const char* file2,char* file3,int n_samples)
{
	FILE *f1=fopen(file1,"r");
	FILE *f2=fopen(file2,"r");
	FILE *f3=fopen(file3,"r");
	FILE *f4=fopen("feature_main.txt","w");

	float x1,x2,x3;
	char ch;

	if( !f1 || !f2 || !f3 || !f4 )
    {
        printf("ERROR: Opening training files.\n");
        exit(0); // all not OK
    }
	for(int line = 0; line < n_samples; line++)
	{

		fscanf(f1, "%f,", &x1);
		fscanf(f2, "%f,", &x2);
		fscanf(f3, "%f,", &x3);
		if (x3==1.0)
			ch='R';
		else
			ch='N';
		fprintf(f4,"%c %f %f\n",ch,x1,x2);
	}
	fclose(f1);fclose(f2);fclose(f3);fclose(f4);
}

int read_data_from_csv(const char* filename, Mat data, Mat classes, int n_samples )
{
    char tmpc;
    float tmpf;

    // if we can't read the input file then return 0
    FILE* f = fopen( filename, "r" );
    if( !f )
    {
        printf("ERROR: cannot read file %s\n",  filename);
        return 0; // all not OK
    }
    // for each sample in the file
    for(int line = 0; line < n_samples; line++)
    {        // for each attribute on the line in the file
        for(int attribute = 0; attribute < (ATTRIBUTES_PER_SAMPLE + 1); attribute++)
        {
            if (attribute == 0.0)
            {
                // attribute 0 (in the database) is the classification
                // record 1 = R= rain
                // record 0 = N= non rain
                fscanf(f, "%c,", &tmpc);
                switch(tmpc)
                {
                case 'R':
                    classes.at<float>(line, 0) = 1.0;
                    break;
                case 'N':
                    classes.at<float>(line, 0) = 0.0;
                    break;
                default:
                    printf("ERROR: unexpected class in file %s\n",  filename);
                    return 0; // all not OK
                }
                // printf("%c,", tmpc);
            }
            else
            {
                fscanf(f, "%f,", &tmpf);
                data.at<float>(line, (attribute - 1)) = tmpf;
                //printf("%f,", tmpf);
            }
        }
        fscanf(f, "\n");
        //printf("\n");
    }
    fclose(f);
    return 1; // all OK
}

void createlabel(char* file1,char* file2)
{
	FILE *f1=fopen(file1,"r");
	FILE *f2=fopen(file2,"r");	
	FILE *f3=fopen("label_main.txt","w");

	float x1,x2,x3;
	int ch;

	if( !f1 || !f2 || !f3 )
    {
        printf("ERROR: Opening training files.\n");
        exit(0); // all not OK
    }
	for(int line = 0; line < NUMBER_OF_TESTING_SAMPLES; line++)
	{

		fscanf(f1, "%f,", &x1);
		fscanf(f2, "%f,", &x2);
		
		if (x1<=20.0 && x2<=2)
			ch= 1 ;
		else
			ch=0;

		fprintf(f3,"%d\n",ch);
	}
	fclose(f1);fclose(f2);fclose(f3);
}

void classifier(Mat training_data_main,Mat training_classifications_main,Mat testing_data_main,Mat testing_classifications_main)
{
	if (read_data_from_csv("feature_main.txt", training_data_main, training_classifications_main, NUMBER_OF_TRAINING_SAMPLES) && read_data_from_csv("feature_main.txt", testing_data_main, testing_classifications_main, NUMBER_OF_TESTING_SAMPLES))
    {
        // train bayesian classifier (using training data)
        //printf( "\nUsing training database: %s\n\n", "feature_main.txt");
        CvNormalBayesClassifier *bayes = new CvNormalBayesClassifier;
        bayes->train(training_data_main, training_classifications_main, Mat(), Mat(), false);

        // perform classifier testing and report results

        Mat test_sample;
        int correct_class = 0;
        int wrong_class = 0;
        int false_positives [NUMBER_OF_CLASSES];
        char class_labels[NUMBER_OF_CLASSES];
        float result;

        // zero the false positive counters in a simple loop

        for (int i = 0; i < NUMBER_OF_CLASSES; i++)
        {
            false_positives[i] = 0;
            class_labels[i] = (char) 65 + i; // ASCII 65 = A
        }

        printf( "\nUsing testing database: %s\n\n", "feature.txt");

        for (int tsample = 0; tsample < NUMBER_OF_TESTING_SAMPLES; tsample++)
        {
            // extract a row from the testing matrix
            test_sample = testing_data_main.row(tsample);

            // run decision tree prediction
            result = bayes->predict(test_sample);

            //printf("Testing Sample %i -> class result (character %c)\n", tsample,CLASSES[((int) result)]);

            // if the prediction and the (true) testing classification are the same
            // (N.B. openCV uses a floating point decision tree implementation!)

            if (fabs(result - testing_classifications_main.at<float>(tsample, 0)) >= FLT_EPSILON)
            {
                // if they differ more than floating point error => wrong class
                wrong_class++;
                false_positives[((int) result)]++;
            }
            else
            {
                correct_class++;
            }
        }
        printf( "\nResults on the testing database: %s\n"
                "\tCorrect classification: %d (%g%%)\n"
                "\tWrong classifications: %d (%g%%)\n",
                "feature.txt",correct_class, (double) correct_class*100/NUMBER_OF_TESTING_SAMPLES,wrong_class, (double) wrong_class*100/NUMBER_OF_TESTING_SAMPLES);

        for (int i = 0; i < NUMBER_OF_CLASSES; i++)
        {
            printf( "\tClass (character %c) false postives 	%d (%g%%)\n", CLASSES[i],false_positives[i],(double) false_positives[i]*100/NUMBER_OF_TESTING_SAMPLES);
        }
	}
}

int countlines(char* fn)
{
	int number_of_lines = 0;
    string line;
    ifstream myfile(fn);

    while (getline(myfile, line))
	{
        ++number_of_lines;
	}

    cout << "Number of lines in text file: " << number_of_lines;

	return number_of_lines;
}

int main(int argc, char** argv)
{
    VideoCapture cap(argv[1]); // open the file
    if(!cap.isOpened())  // check if we succeeded
        return -1;

    /////////////////////////////////////// Variable Declaration /////////////////////////////////////////////////////

    Mat frame;
    Mat edges;
    Size image_size((int)cap.get(CV_CAP_PROP_FRAME_WIDTH),(int)cap.get(CV_CAP_PROP_FRAME_HEIGHT));
    Mat cur_temp=Mat::zeros(image_size,CV_8UC1);
    Mat cur_temp_smooth=Mat::zeros(image_size,CV_8UC1);
    Mat prev_temp=Mat::zeros(image_size,CV_8UC1);    
    Mat cur_edge=Mat::zeros(image_size,CV_8UC1);
    Mat prev_edge=Mat::zeros(image_size,CV_8UC1);
    Mat next_edge=Mat::zeros(image_size,CV_8UC1);
	Mat next_temp=Mat::zeros(image_size,CV_8UC1);
	Mat prev_temp2=Mat::zeros(image_size,CV_8UC1);
	Mat next_temp2=Mat::zeros(image_size,CV_8UC1);
    Mat img_diff;
    Mat diff3 = Mat::zeros(image_size,CV_8UC1);
    Mat gradx = Mat::zeros(image_size,CV_8UC1); 
	Mat grady = Mat::zeros(image_size,CV_8UC1); 
	Mat grad = Mat::zeros(image_size,CV_8UC1); 
    Mat mask = Mat::zeros(image_size,CV_8UC1); 
	Mat background = Mat::zeros(image_size,CV_8UC1);
    Mat diff_staticedge = Mat::zeros(image_size,CV_8UC1); 
    Mat diff_moveedge = Mat::zeros(image_size,CV_8UC1); 
    Mat recon_image = Mat::zeros(image_size,CV_8UC1); 
    Mat range = Mat::zeros(image_size,CV_8UC1);
    Mat spreadsymm = Mat::zeros(image_size,CV_8UC1);
    Mat classify = Mat::zeros(image_size,CV_8UC1);
	Mat samplesmat = Mat::zeros(image_size,CV_32FC2);
	Mat result_classifier=Mat::zeros(image_size,CV_8UC1);

    vector<float> tpf,tnf;
    
    float mean,T1,T2,below_std,above_std;
	clock_t t1,t2;
    float diff=0,time_frame=0;
	char* cthreshold=argv[2];
	char* cratio=argv[3];
	float threshold=atof(cthreshold);
	float ratio=atof(cratio);
	int no_frames=0;
    T1=40;
    T2=4;
	float max,min;	
	float a[5]={0.0,0.0,0.0,0.0,0.0},ss=0.0,rangex=0.0;

	uchar* data_spreadsymm = (uchar*)spreadsymm.data;
	uchar* data_range=(uchar*)range.data;
	uchar* data_cur=(uchar*)cur_temp.data;
    uchar* data_prev=(uchar*)prev_temp.data;
	uchar* data_prev2=(uchar*)prev_temp2.data;
    uchar* data_next=(uchar*)next_temp.data;
	uchar* data_next2=(uchar*)next_temp2.data;
    uchar* data_diff3=(uchar*)diff3.data;
	uchar* data_grad=(uchar*)grad.data;
	int width_step=cur_temp.step1();
    
    /////////////////////////////////////////// File Opening and setting /////////////////////////////////////////////////////

	ofstream res_file,range_file,ss_file;

    res_file.open("file.txt");
	range_file.open("range_main.txt",ios::out);
	ss_file.open("ss_main.txt",ios::out);
	if( res_file.is_open() && range_file.is_open() && ss_file.is_open() )
	{
        cout<<"Files open.\n";
        res_file<<"Results:\n";
	}
	else	
		cout<<"Unable to open the file.\n";

	const string source      = argv[1];           // the source file name
    const bool askOutputType = argv[4][0] =='Y';  // If false it will use the inputs codec type
	string::size_type pAt = source.find_last_of('.');                  // Find extension point
    const string NAME = source.substr(0, pAt) + "_output" + ".avi";   // Form the new name with container
    int ex = static_cast<int>(cap.get(CV_CAP_PROP_FOURCC));     // Get Codec Type- Int form

    // Transform from int to char via Bitwise operators
    char EXT[] = {(char)(ex & 0XFF) , (char)((ex & 0XFF00) >> 8),(char)((ex & 0XFF0000) >> 16),(char)((ex & 0XFF000000) >> 24), 0};

    Size S = Size((int) cap.get(CV_CAP_PROP_FRAME_WIDTH),    // Acquire input size
                  (int) cap.get(CV_CAP_PROP_FRAME_HEIGHT));

    VideoWriter outputVideo;                                        // Open the output
    if (askOutputType)
        outputVideo.open(NAME, ex=-1, cap.get(CV_CAP_PROP_FPS), S, true);
    else
        outputVideo.open(NAME, ex, cap.get(CV_CAP_PROP_FPS), S, true);

    if (!outputVideo.isOpened())
    {
        cout  << "Could not open the output video for write: " << source << endl;
        return -1;
    }

    cout << "Input frame resolution: Width=" << S.width << "  Height=" << S.height << " of nr#: " << cap.get(CV_CAP_PROP_FRAME_COUNT) << endl;
    cout << "Input codec type: " << EXT << endl;

	////////////////////////////////////////////////////// Training ////////////////////////////////////////////////////////

	Mat training_data = Mat(NUMBER_OF_TRAINING_SAMPLES, ATTRIBUTES_PER_SAMPLE, CV_32FC1);
    Mat training_classifications = Mat(NUMBER_OF_TRAINING_SAMPLES, 1, CV_32FC1);

	//define testing data storage matrices

    Mat testing_data = Mat(NUMBER_OF_TESTING_SAMPLES, ATTRIBUTES_PER_SAMPLE, CV_32FC1);
    Mat testing_classifications = Mat(NUMBER_OF_TESTING_SAMPLES, 1, CV_32FC1);

	mergefile(argv[5],argv[6],"label.txt",NUMBER_OF_TRAINING_SAMPLES);

	if (read_data_from_csv("feature.txt", training_data, training_classifications, NUMBER_OF_TRAINING_SAMPLES) &&
            read_data_from_csv("feature.txt", testing_data, testing_classifications, NUMBER_OF_TESTING_SAMPLES))
    {
        // train bayesian classifier (using training data)
        printf( "\nUsing training database: %s\n\n", "feature.txt");
        CvNormalBayesClassifier *bayes = new CvNormalBayesClassifier;
        bayes->train(training_data, training_classifications, Mat(), Mat(), false);

        // perform classifier testing and report results

        Mat test_sample;
        int correct_class = 0;
        int wrong_class = 0;
        int false_positives [NUMBER_OF_CLASSES];
        char class_labels[NUMBER_OF_CLASSES];
        float result;

        // zero the false positive counters in a simple loop

        for (int i = 0; i < NUMBER_OF_CLASSES; i++)
        {
            false_positives[i] = 0;
            class_labels[i] = (char) 65 + i; // ASCII 65 = A
        }

        printf( "\nUsing testing database: %s\n\n", "feature.txt");

        for (int tsample = 0; tsample < NUMBER_OF_TESTING_SAMPLES; tsample++)
        {
            // extract a row from the testing matrix
            test_sample = testing_data.row(tsample);

            // run decision tree prediction
            result = bayes->predict(test_sample);

            printf("Testing Sample %i -> class result (character %c)\n", tsample,CLASSES[((int) result)]);

            // if the prediction and the (true) testing classification are the same
            // (N.B. openCV uses a floating point decision tree implementation!)

            if (fabs(result - testing_classifications.at<float>(tsample, 0)) >= FLT_EPSILON)
            {
                // if they differ more than floating point error => wrong class
                wrong_class++;
                false_positives[((int) result)]++;
            }
            else
            {
                correct_class++;
            }
        }
        printf( "\nResults on the testing database: %s\n"
                "\tCorrect classification: %d (%g%%)\n"
                "\tWrong classifications: %d (%g%%)\n",
                "feature.txt",correct_class, (double) correct_class*100/NUMBER_OF_TESTING_SAMPLES,wrong_class, (double) wrong_class*100/NUMBER_OF_TESTING_SAMPLES);

        for (int i = 0; i < NUMBER_OF_CLASSES; i++)
        {
            printf( "\tClass (character %c) false postives 	%d (%g%%)\n", CLASSES[i],false_positives[i],(double) false_positives[i]*100/NUMBER_OF_TESTING_SAMPLES);
        }
	}

    /////////////////////////////////////////////////////////////////////// Code ////////////////////////////////////////////////////////////////////////////////

    for( ; ; )
    {
		no_frames++;
        cap>>frame; 
        
		if(frame.empty()){break;}
        imshow("current video", cur_temp); 
		
        cvtColor(frame, next_temp2, CV_BGR2GRAY);
        
		t1=clock();
                                  
  //////////////////////////////////////////////////  candidate rain pixels ////////////////////////////////////////////////        
      if(no_frames>=5)
	  {
        for(int i=0;i<image_size.height;i++)
        {
			for(int j=0; (j<((0.2)*image_size.width));j++)
           {
             int k_cur = data_cur[i*width_step+j];
             int k_pre = data_prev[i*width_step+j];
             int k_next = data_next[i*width_step+j];             
             if(((k_cur-k_pre)>=7) || ((k_cur-k_next)>=7))//|| (((2*k_cur-k_pre-k_next)==0)))
              {
                data_diff3[i*width_step+j]=255;
              } 
             else
                data_diff3[i*width_step+j]=0;                           
           }
         }

		for(int i=0;i<image_size.height;i++)
        {
			for(int j=(image_size.width-1);j>=((0.8)*image_size.width);j--)
           {
             int k_cur = data_cur[i*width_step+j];
             int k_pre = data_prev[i*width_step+j];
             int k_next = data_next[i*width_step+j];             
             if(((k_cur-k_pre)>=7) || ((k_cur-k_next)>=7))//|| (((2*k_cur-k_pre-k_next)==0)))
              {
                data_diff3[i*width_step+j]=255;
              } 
             else
                data_diff3[i*width_step+j]=0;                           
           }
         }
        
		for(int i=0;i<(0.2)*image_size.height;i++)
        {
			for(int j=(0.2*image_size.width);j<(0.8*image_size.width);j++)
           {
             int k_cur = data_cur[i*width_step+j];
             int k_pre = data_prev[i*width_step+j];
             int k_next = data_next[i*width_step+j];             
             if(((k_cur-k_pre)>=7) || ((k_cur-k_next)>=7))//|| (((2*k_cur-k_pre-k_next)==0)))
              {
                data_diff3[i*width_step+j]=255;
              } 
             else
                data_diff3[i*width_step+j]=0;                           
           }
        }

        //imshow("diff", diff3);       // All rain pixels
        
    //////////////////////////////////////////// static edge detection and removal /////////////////////////////////////////
    
        boxFilter(cur_temp,cur_temp_smooth,8, Size(3,3)); // changed from 5,5
        Canny(prev_temp,prev_edge,threshold,ratio*threshold,3);              // Threshold =10 Ratio =12.5
        Canny(next_temp,next_edge,threshold,ratio*threshold,3);
        Canny(cur_temp,cur_edge,threshold,ratio*threshold,3);

        uchar* data_prev_edge=(uchar*)prev_edge.data;
        uchar* data_next_edge=(uchar*)next_edge.data;
        uchar* data_cur_edge=(uchar*)cur_edge.data;
		uchar* data_diff_staticedge=(uchar*)diff_staticedge.data;
        uchar* data_edge=(uchar*)edges.data;
        
        Canny(cur_temp_smooth, edges,threshold,ratio*threshold,3); 

		//imshow("Edges",edges);
        
        for(int i=0;i<image_size.height;i++)
        {
			for(int j=0; (j<((0.2)*image_size.width));j++)
           {
           if((data_prev_edge[i*width_step+j]==255)&&(data_cur_edge[i*width_step+j]==255)&&(data_next_edge[i*width_step+j]==255))            
              data_diff_staticedge[i*width_step+j]=255;  
           else
              data_diff_staticedge[i*width_step+j]=0;               
           }
        } 

        for(int i=0;i<image_size.height;i++)
        {
			for(int j=(image_size.width-1);j>=((0.8)*image_size.width);j--)
           {
           if((data_prev_edge[i*width_step+j]==255)&&(data_cur_edge[i*width_step+j]==255)&&(data_next_edge[i*width_step+j]==255))            
              data_diff_staticedge[i*width_step+j]=255;  
           else
              data_diff_staticedge[i*width_step+j]=0;               
           }
        } 

		for(int i=0;i<(0.2)*image_size.height;i++)
        {
			for(int j=(0.2*image_size.width);j<(0.8*image_size.width);j++)
           {
           if((data_prev_edge[i*width_step+j]==255)&&(data_cur_edge[i*width_step+j]==255)&&(data_next_edge[i*width_step+j]==255))            
              data_diff_staticedge[i*width_step+j]=255;  
           else
              data_diff_staticedge[i*width_step+j]=0;               
           }
        } 
        
        for(int i=0;i<image_size.height;i++)
        {
          for(int j=0;j<image_size.width;j++)
           {              
                data_diff3[i*width_step+j]=data_diff3[i*width_step+j]-data_diff_staticedge[i*width_step+j];                                       
           }
        }
        
        std::vector<std::vector<Point> > contour;
        findContours(diff3,contour,CV_RETR_LIST,CV_CHAIN_APPROX_NONE);
        Mat cont = Mat::zeros(image_size,CV_8UC3);
        std::vector<std::vector<Point> > contour_boundary;
        
        for(int k=0;k<contour.size();k++)
        {
          double area = contourArea(contour[k]);
          if(area>=8 && area<=40)
          {
            contour_boundary.push_back(contour[k]);            
          }
        } 
        
        drawContours(cont,contour_boundary,-1,Scalar(255,255,255),-1);
        
        std::vector<Mat> cont_filled;
        split(cont,cont_filled);        
        
        diff3=diff3-cont_filled[0];
		Mat element = getStructuringElement( 2, Size( 3, 3 ), Point( 1, 1 ) );

        /// Apply the dilation and erosion
        dilate( diff3, diff3, element );
		erode (diff3,diff3,element);

		//Apply scharr operator in x and y direction

		Scharr( diff3, gradx, diff3.depth(), 1, 0, 1, 0, BORDER_DEFAULT );
		convertScaleAbs( gradx,gradx );
		Scharr( diff3, grady, diff3.depth(), 0, 1, 1, 0, BORDER_DEFAULT );
		convertScaleAbs( grady,grady);
		
		addWeighted( gradx, 0.5, grady, 0.5, 0, grad );
        
		//imshow("diff_refined",diff3);        
        //imshow("contour",cont);         

        
   ////////////////////////////////////////////////// moving edge detection ////////////////////////////////////////////     
        
        cur_edge.copyTo(diff_moveedge);

        uchar* data_diff_moveedge=(uchar*)diff_moveedge.data;
        
        for(int i=0;i<image_size.height;i++)
        {
			for(int j=0; (j<((0.2)*image_size.width));j++)
            {
                 int temp=data_cur_edge[i*width_step+j];
                 int temp_pre=data_prev_edge[i*width_step+j];
                 int temp_next=data_next_edge[i*width_step+j];
                 if ((temp==temp_pre) && (temp==temp_next))
                 {
                    data_diff_moveedge[i*width_step+j]=0;
                 }
            }
        }

		for(int i=0;i<image_size.height;i++)
        {
			for(int j=(image_size.width-1);j>=((0.8)*image_size.width);j--)
           {
                 int temp=data_cur_edge[i*width_step+j];
                 int temp_pre=data_prev_edge[i*width_step+j];
                 int temp_next=data_next_edge[i*width_step+j];
                 if ((temp==temp_pre) && (temp==temp_next))
                 {
                    data_diff_moveedge[i*width_step+j]=0;
                 }
            }
        }

		for(int i=0;i<(0.2)*image_size.height;i++)
        {
			for(int j=(0.2*image_size.width);j<(0.8*image_size.width);j++)
           {
                 int temp=data_cur_edge[i*width_step+j];
                 int temp_pre=data_prev_edge[i*width_step+j];
                 int temp_next=data_next_edge[i*width_step+j];
                 if ((temp==temp_pre) && (temp==temp_next))
                 {
                    data_diff_moveedge[i*width_step+j]=0;
                 }
            }
        }
        
		dilate( diff_moveedge, diff_moveedge, element );
		//erode(diff_moveedge,diff_moveedge,element);

        diff3=diff3-diff_moveedge; 
       
		//diff4=next_temp-diff3;
                 
        //imshow("diff_moveedge",diff_moveedge);
        //imshow("Only edges",diff3);

		//Edges 
		//imshow("x edges",gradx);
		//imshow("y edges",grady);
		//imshow("edges after scharr",grad);

        grad=grad-diff_moveedge;

		//imshow("edges after removing moving edge",grad);

   //////////////////////////////////////////////////// Feature Extraction ////////////////////////////////////////////////
  
	for(int i=0;i<image_size.height;i++)
        {
		for(int j=0; (j<((0.2)*image_size.width));j++)
            {
				a[0]=data_prev2[i*width_step+j];
				a[1]=data_prev[i*width_step+j];
				a[2]=data_cur[i*width_step+j];
				a[3]=data_next[i*width_step+j];
				a[4]=data_next2[i*width_step+j];

				max= maximum(a);
				min= minimum(a);	
				rangex=max-min;
				range_file<<rangex<<endl;

				ss=spreads(a);
				ss_file<<ss<<endl;

				data_spreadsymm[i*width_step+j]=ss;
				data_range[i*width_step+j]=rangex;        
			}			
		}

	for(int i=0;i<image_size.height;i++)
        {
			for(int j=(image_size.width-1);j>=((0.8)*image_size.width);j--)
           {
				a[0]=data_prev2[i*width_step+j];
				a[1]=data_prev[i*width_step+j];
				a[2]=data_cur[i*width_step+j];
				a[3]=data_next[i*width_step+j];
				a[4]=data_next2[i*width_step+j];

				max= maximum(a);
				min= minimum(a);	
				rangex=max-min;
				range_file<<rangex<<endl;

				ss=spreads(a);
				ss_file<<ss<<endl;

				data_spreadsymm[i*width_step+j]=ss;
				data_range[i*width_step+j]=rangex;        
			}			
		}

	 for(int i=0;i<(0.2)*image_size.height;i++)
        {
			for(int j=(0.2*image_size.width);j<(0.8*image_size.width);j++)
           {
				a[0]=data_prev2[i*width_step+j];
				a[1]=data_prev[i*width_step+j];
				a[2]=data_cur[i*width_step+j];
				a[3]=data_next[i*width_step+j];
				a[4]=data_next2[i*width_step+j];

				max= maximum(a);
				min= minimum(a);	
				rangex=max-min;
				range_file<<rangex<<endl;

				ss=spreads(a);
				ss_file<<ss<<endl;

				data_spreadsymm[i*width_step+j]=ss;
				data_range[i*width_step+j]=rangex;        
			}			
		}

	 range_file.close();
		ss_file.close();
    //imshow("Range",range);
    //imshow("Spreadsymmetry",spreadsymm);

   //////////////////////////////////////////////////// Classification ////////////////////////////////////////////////////

	 int u=countlines("range_main.txt");
	 //int v=countlines("ss_main.txt");

	 Mat training_data_main = Mat(u, ATTRIBUTES_PER_SAMPLE, CV_32FC1);
     Mat training_classifications_main = Mat(u, 1, CV_32FC1);

	 //define testing data storage matrices

     Mat testing_data_main = Mat(u, ATTRIBUTES_PER_SAMPLE, CV_32FC1);
     Mat testing_classifications_main = Mat(u, 1, CV_32FC1);

	 createlabel("range_main.txt","ss_main.txt");

	 mergefile_main("range_main.txt","ss_main.txt","label_main.txt",u);

	 classifier(training_data_main,training_classifications_main,testing_data_main,testing_classifications_main);	 

	 uchar* data_classify = (uchar*)classify.data;
   
	 for(int i=0;i<image_size.height;i++)
	 {
	  for(int j=0;j<image_size.width;j++)
		{
		if( (data_range[i*width_step+j]>8) && (data_spreadsymm[i*width_step+j]>10))
			data_classify[i*width_step+j]=255;

			data_classify[i*width_step+j]=255-data_classify[i*width_step+j];
		}    
	 }

	 /*for(int i=0;i<image_size.height;i++)
        {
		for(int j=0; (j<((0.2)*image_size.width));j++)
            {
				if( (data_range[i*width_step+j]>8) && (data_spreadsymm[i*width_step+j]>10))
				data_classify[i*width_step+j]=255;

				data_classify[i*width_step+j]=255-data_classify[i*width_step+j];
			}    
		}

	 for(int i=0;i<image_size.height;i++)
        {
			for(int j=(image_size.width-1);j>=((0.8)*image_size.width);j--)
           {
				if( (data_range[i*width_step+j]>8) && (data_spreadsymm[i*width_step+j]>10))
				data_classify[i*width_step+j]=255;

				data_classify[i*width_step+j]=255-data_classify[i*width_step+j];
		   }    
        }

     for(int i=0;i<(0.2)*image_size.height;i++)
        {
			for(int j=(0.2*image_size.width);j<(0.8*image_size.width);j++)
           {
			if( (data_range[i*width_step+j]>8) && (data_spreadsymm[i*width_step+j]>10))
			data_classify[i*width_step+j]=255;

			data_classify[i*width_step+j]=255-data_classify[i*width_step+j];
		   }    
		}

    //bayes->train(samplesmat,classify);

    //result_classifier=bayes->predict(diff3);

    imshow("Classifier",classify);*/

   
    //////////////////////////////////////////////////// Image Inpainting /////////////////////////////////////////////////
        
		//ofstream recon;

		//recon.open("classifeid.txt");

        uchar* data_reconimg=(uchar*)recon_image.data;

		/*for(int p=0;p<image_size.height;p++)
        {
			for(int q=0;q<image_size.width;q++)
            {
            //if((data_grad[p*width_step+q])||data_classify[p*width_step+q])
			if(data_classify[p*width_step+q])
              { 
                data_reconimg[p*width_step+q]=(data_prev[p*width_step+q]+data_next[p*width_step+q]+data_prev2[p*width_step+q]+data_next2[p*width_step+q])/4;                
              }
              else
              {
                data_reconimg[p*width_step+q]=data_cur[p*width_step+q];
              }               
            }
        }*/
        
        for(int p=0;p<image_size.height;p++)
        {
			for(int q=0; (q<((0.2)*image_size.width));q++)
            {
            //if((data_grad[p*width_step+q])||data_classify[p*width_step+q])
			if(data_classify[p*width_step+q])
              { 
                data_reconimg[p*width_step+q]=(data_prev[p*width_step+q]+data_next[p*width_step+q]+data_prev2[p*width_step+q]+data_next2[p*width_step+q])/4;                
              }
              else
              {
                data_reconimg[p*width_step+q]=data_cur[p*width_step+q];
              }               
            }
        }

		for(int p=0;p<image_size.height;p++)
        {
			for(int q=(image_size.width-1);q>=((0.8)*image_size.width);q--)
           {
            //if((data_grad[p*width_step+q])||data_classify[p*width_step+q])
			if(data_classify[p*width_step+q])
              { 
                data_reconimg[p*width_step+q]=(data_prev[p*width_step+q]+data_next[p*width_step+q]+data_prev2[p*width_step+q]+data_next2[p*width_step+q])/4;                
              }
              else
              {
                data_reconimg[p*width_step+q]=data_cur[p*width_step+q];
              }               
            }
        }

		for(int p=0;p<(0.2)*image_size.height;p++)
        {
			for(int q=(0.2*image_size.width);q<(0.8*image_size.width);q++)
           {
            //if((data_grad[p*width_step+q])||data_classify[p*width_step+q])
			if(data_classify[p*width_step+q])
              { 
                data_reconimg[p*width_step+q]=(data_prev[p*width_step+q]+data_next[p*width_step+q]+data_prev2[p*width_step+q]+data_next2[p*width_step+q])/4;                
              }
              else
              {
                data_reconimg[p*width_step+q]=data_cur[p*width_step+q];
              }               
            }
        }
       // GaussianBlur(recon_image,recon_image,Size(1,1),1.5,1.5);

  ////////////////////////////////////////////// Writing time in file ////////////////////////////////////////////////////
		t2=clock();
        diff=float(t2-t1)/CLOCKS_PER_SEC;
        time_frame+= diff;
        res_file<<"Time for frame "<<no_frames<<" is: "<<diff<<" s."<<endl;
        
   ////////////////////////////////////////////////// writing video file /////////////////////////////////////////////////

		imshow("Reconstructed Image",recon_image);
        outputVideo << recon_image;

	  }

   ///////////////////////////////////////////////// Taking next frame ///////////////////////////////////////////////////             

		prev_temp.copyTo(prev_temp2);
        cur_temp.copyTo(prev_temp);
        next_temp.copyTo(cur_temp);
		next_temp2.copyTo(next_temp);
                      
        if(waitKey(50) >= 0) break;
    }

    cap.release();
    res_file<<"Average Time per frame:"<<time_frame/no_frames<<endl;
    res_file.close();
	
    return 0;
}